package composer

import (
	"context"
	"fmt"
	"reflect"

	"gddgame.cc/galaxy/utils/def"
)

type Builder interface {
	def.Context
	Basis

	IsDebug() bool
	Ctx() context.Context

	BindInstance(key string, instance interface{}) error
	Factory(key string, instance interface{}) bool

	Context(id string) *Context
}

type builder struct {
	index int
	def.Logger
	def.ExpireStore
	Basis

	*composer
}

func (b *builder) ID() string {
	return "builder"
}

func (b *builder) IsDebug() bool {
	return b.composer.debug
}

func (b *builder) Ctx() context.Context {
	return b.composer.ctx
}

func (b *builder) BindInstance(key string, instance interface{}) error {
	key = fmt.Sprintf("%s/%d", key, b.index)
	if _, ok := b.composer.instances[key]; ok {
		return fmt.Errorf("[ Register ] %s Instance is exists", key)
	}
	b.composer.instances[key] = instance
	b.composer.logger.Debugf("[ Builder ] Bind Instance: %#v", key)
	return nil
}

func (b *builder) Factory(key string, instance interface{}) bool {
	key = fmt.Sprintf("%s/%d", key, b.index)
	v, ok := b.composer.instances[key]
	if !ok {
		b.Debugf("[ Builder ] Factory miss %s", key)
		return false
	}

	vv := reflect.ValueOf(v)
	if vv.IsNil() {
		b.Debugf("[ Builder ] Factory instance is nil %s", key)
		return false
	}
	it := reflect.TypeOf(instance)
	if it.Kind() != reflect.Ptr {
		b.Debugf("[ Builder ] Factory instance must Ptr %s", key)
		return false
	}

	ite := it.Elem()
	if ok := vv.Type().AssignableTo(ite); !ok {
		b.Debugf("[ Builder ] Factory instance type error %s %s=>%s", key, vv.Type(), ite)
		return false
	}

	vi := reflect.ValueOf(instance).Elem()
	vi.Set(vv)
	b.composer.logger.Debugf("[ Builder ] Factory Instance: %#v", key)
	return true
}

func (b *builder) Context(id string) *Context {
	context := b.contextPool.Get().(*Context)
	context.init(id)
	return context
}
